package com.onpositive.data.diskstorage;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.FilterTester;
import com.onpositive.data.IObjectCollection;
import com.onpositive.data.IPropertyMetadata;
import com.onpositive.data.serializer.readers.DefaultReaderFactory;
import com.onpositive.data.serializer.readers.StringIterableReader;
import com.onpositive.data.serializer.readers.StringReader;
import com.onpositive.datamap.IDataPropertyValue;
import com.onpositive.datamap.core.IDataObject;
import com.onpositive.datamap.core.impl.AbstractDataProperty;
import com.onpositive.datamap.core.impl.DataObject;
import com.onpositive.datamap.item.SingleDataObject;
import com.onpositive.semantic.model.api.property.IProperty;

public abstract class AbstractObjectCollection extends AbstractValueAccessor implements
		IObjectCollection<Integer> {

	protected ArrayList<IPropertyMetadata> metadataArray = new ArrayList<IPropertyMetadata>();

	protected HashMap<IPropertyMetadata, AbstractReader<?>> readerMap = new HashMap<IPropertyMetadata, AbstractReader<?>>();
	protected HashMap<IPropertyMetadata, Integer> metadataIndexMap = new HashMap<IPropertyMetadata, Integer>();

	protected IPostInitReader readerInit;

	public IPostInitReader getReaderInit() {
		return readerInit;
	}

	public void setReaderInit(IPostInitReader readerInit) {
		this.readerInit = readerInit;
	}
	
	@Override
	public void setValue(IDataObject obj, AbstractDataProperty p,Object value) {
		
		DataObject ob = (DataObject) obj;
		if (ob==null){
			throw new IllegalArgumentException();
		}
		AbstractReader<?> reader = readerMap.get(p.meta);
		if (reader == null)
			reader = initReader(p.meta);
		if (reader == null) {
			throw new IllegalArgumentException();
		}
		reader.setValue(ob.num,value);
	}
	
	
	public IDataPropertyValue getValue(IDataObject object,AbstractDataProperty p) {
		if (object instanceof SingleDataObject){
			SingleDataObject m=(SingleDataObject) object;
			IProperty property = m.getProperty(m,p.getId());
			if (property!=null){
				return (IDataPropertyValue) property.getValue(m);
			}
			return null;
		}
		DataObject obj = (DataObject) object;
		if (obj==null){
			return null;
		}
		Object value = getValue(obj.num, p.meta);
		if (value instanceof IDataPropertyValue){
			return (IDataPropertyValue) value;
		}
		return p.convert(value);
	}


	protected void recordReader(IPropertyMetadata meta, AbstractReader<?> result) {
		IPostInitReader readerInit2 = getReaderInit();
		if (readerInit2 != null) {
			readerInit2.configure(meta, result);
		}
		readerMap.put(meta, result);
	}

	public FilterTester getFilterTester(String propId, Object filterConstraint,
			String filterKind) {
		IPropertyMetadata property = getProperty(propId);
		AbstractReader<?> reader = readerMap.get(property);

		if (reader == null)
			reader = initReader(property);
		if (reader == null) {
			return null;
		}
		return reader;
	}

	protected int dataSize;

	public void addNewMetadata(IPropertyMetadata mtd) {
		metadataIndexMap.put(mtd, -1);
		metadataArray.add(mtd);
		
	}
	public void addMetadata(IPropertyMetadata mtd) {
		metadataIndexMap.put(mtd, metadataIndexMap.size());
		metadataArray.add(mtd);
		
	}

	@Override
	public int size() {
		return dataSize;
	}

	public AbstractObjectCollection(int dataSize) {
		super();
		this.dataSize = dataSize;
	}

	@Override
	public Class<Integer> getObjectClass() {
		return Integer.class;
	}

	@Override
	public Object getValue(Integer obj, IPropertyMetadata meta) {
		if (obj == null) {
			return null;
		}
		AbstractReader<?> reader = readerMap.get(meta);

		if (reader == null)
			reader = initReader(meta);
		if (reader == null) {
			return null;
		}
		return reader.getObject(obj);
	}

	@Override
	public Iterator<Integer> iterator() {
		return new Iterator<Integer>() {

			int a = 0;

			@Override
			public boolean hasNext() {
				return a < size();
			}

			@Override
			public Integer next() {
				return a++;
			}

			@Override
			public void remove() {

			}
		};
	}

	@Override
	public Collection<IPropertyMetadata> properties() {
		return this.metadataArray;
	}

	public Integer getItem(int position) {
		return position;
	}

	public IPropertyMetadata getProperty(String textContentPropertyName) {
		for (IPropertyMetadata m : metadataArray) {
			if (m.id().equals(textContentPropertyName)) {
				return m;
			}
		}
		return null;
	}

	protected abstract AbstractReader<?> initReader(IPropertyMetadata meta);

	public void filterOut(LinkedHashSet<Object> obj, String text) {
		LinkedHashSet<Object> ma = new LinkedHashSet<Object>();
		for (IPropertyMetadata m : metadataArray) {
			LinkedHashSet<Object> mq = new LinkedHashSet<Object>(obj);
			AbstractReader<?> reader = readerMap.get(m);

			if (reader == null)
				reader = initReader(m);
			if (reader == null) {
				continue;
			}
			if (reader instanceof StringReader) {
				reader.filterOut(mq, text);
				ma.addAll(mq);
			}
			if (reader instanceof StringIterableReader) {
				reader.filterOut(mq, text);
				ma.addAll(mq);
			}

		}
	}

	public void init(IPropertyMetadata mainImage) {
		AbstractReader<?> initReader = initReader(mainImage);
		if (initReader!=null){
			 initReader.init();
		}
	}

	public void write(DataOutputStream s) throws IOException {
		
	}

	public void removeMetadata(IPropertyMetadata meta) {
		metadataArray.remove(meta);
		metadataIndexMap.remove(meta);
		readerMap.remove(meta);
	}

	protected AbstractReader<?> initLocal(IPropertyMetadata meta, byte[] bs) {
		AbstractReader<?> result = DefaultReaderFactory.getInstance()
				.createReader(Integer.MIN_VALUE, meta);
		if (bs == null) {
			result.initEmpty(dataSize);
			recordReader(meta, result);
			return result;
		}
		result.setFile(new ByteBufferSeekableAcess(bs));
		result.setMyStart(0);
		result.setMyEnd(bs.length);
		recordReader(meta, result);
		return result;
	}

	public IPropertyMetadata[] getAllMetas() {
			return metadataArray.toArray(new IPropertyMetadata[metadataArray.size()]);
	}

	public void setValue(int num,IPropertyMetadata meta,Object value) {
		AbstractReader<?> reader = readerMap.get(meta);
		if (reader == null)
			reader = initReader(meta);
		if (reader == null) {
			throw new IllegalArgumentException();
		}
		reader.setValue(num,value);
	}
	
	
}
